package coordinator

import (
	"adai.design/homemaster/container"
	"adai.design/homemaster/log"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"time"
)

var configFileDefault = func() string {
	if runtime.GOARCH != "mipsle" {
		path := os.Getenv("GOPATH") + "/src/adai.design/homemaster/build/tmp/"
		file := path + "zigbee.cfg"
		return file
	}
	return "/etc/homemaster/zigbee.cfg"
}()

const (
	DevTypeRouter      = "router"
	DevTypeCoordinator = "coordinator"
	DevTypeEndDevice   = "enddevice"
)

const (
	ZigbeeProfileZHA = "0104"
	ZigbeeProfileZLL = "c05e"

	ZigbeeDeviceZLLOnOffLight             = "0000"
	ZigbeeDeviceZLLPlugInUint             = "0010"
	ZigbeeDeviceZLLDimmableLight          = "0100"
	ZigbeeDeviceZLLDimmablePlugInUint     = "0110"
	ZigbeeDeviceZLLColourLight            = "0200"
	ZigbeeDeviceZLLExtendedColourLight    = "0210"
	ZigbeeDeviceZLLColourTemperatureLight = "0220"

	ZigbeeDeviceZHASmartPlug              = "0051"
	ZigbeeDeviceZHAHTSensor               = "5f01"
	ZigbeeDeviceZHAContactSensor          = "0104"
	ZigbeeDeviceZHAMotionSensor           = "0107"
	ZigbeeDeviceZHAColourTemperatureLight = "010c"

	ZigbeeClusterIdBasic = "0000"
	ZigbeeClusterIdOnOff = "0006"
)

const (
	CldBaseAttrIdManufacturerName = "0004"
	CldBaseAttrIdModeIdentifier   = "0005"
	CldBaseAttrIdDateCode         = "0006"
)

type zigbeeInterface interface {
	update(data []byte) error
}

type EndPointDescriptor struct {
	Endpoint  uint8  `json:"endpoint"`
	ProfileId string `json:"profile_id"`
	DeviceId  string `json:"device_id"`

	InputCluster  []string `json:"input_cluster,omitempty"`
	OutputCluster []string `json:"output_cluster,omitempty"`

	Manufacture    string `json:"manufacturer,omitempty"`
	ModeIdentifier string `json:"mode,omitempty"`
	DateCode       string `json:"date,omitempty"`
}

type ZigbeeDescriptor struct {
	NetAddr string `json:"net_addr"`
	MacAddr string `json:"mac_addr"`

	DevType     string                `json:"type"`
	EndPoints   []int                 `json:"endpoints"`
	Descriptors []*EndPointDescriptor `json:"descriptions"`

	instance zigbeeInterface
}

func (a *ZigbeeDescriptor) newInstance() zigbeeInterface {
	for _, descriptor := range a.Descriptors {
		switch descriptor.DeviceId {
		case ZigbeeDeviceZHASmartPlug:
			plug := newZigbeeSmartPlug(a)
			if plug != nil {
				container.AddAccessory(plug.acc.Accessory)
			}
			return plug

		case ZigbeeDeviceZLLExtendedColourLight:
			light := newColorLightLight(a)
			if light != nil {
				container.AddAccessory(light.acc.Accessory)
			}
			return light

		case ZigbeeDeviceZLLColourTemperatureLight, ZigbeeDeviceZHAColourTemperatureLight:
			light := newTemperatureColorLightLight(a)
			if light != nil {
				container.AddAccessory(light.acc.Accessory)
			}
			return light

		case ZigbeeDeviceZHAHTSensor:
			sensor := newHTSensor(a)
			if sensor != nil {
				container.AddAccessory(sensor.acc.Accessory)
			}
			return sensor

		case ZigbeeDeviceZHAContactSensor:
			sensor := newContactSensor(a)
			if sensor != nil {
				container.AddAccessory(sensor.acc.Accessory)
			}
			return sensor

		case ZigbeeDeviceZHAMotionSensor:
			sensor := newMotionSensor(a)
			if sensor != nil && sensor.acc != nil {
				container.AddAccessory(sensor.acc.Accessory)
			}
			return sensor

		case ZigbeeDeviceZLLDimmableLight:
			sw := newzigbeeSwitch(a)
			if sw != nil && sw.acc != nil {
				container.AddAccessory(sw.acc.Accessory)
			}
			return sw

		}
	}
	return nil
}

func (a *ZigbeeDescriptor) activeEndPointRequest() error {
	data, _ := hex.DecodeString(a.NetAddr)
	msg := &Message{MsgT: SerialLinkActiveEndpointRequest, Data: data}
	sdata, err := msg.Encode()
	if err != nil {
		return err
	}
	addTask(sdata)
	return nil
}

func (a *ZigbeeDescriptor) simpleDescriptionRequest(endpoint uint8) error {
	net, _ := hex.DecodeString(a.NetAddr)
	data := make([]byte, 3)
	copy(data[:], net)
	data[2] = endpoint
	msg := &Message{MsgT: SerialLinkMsgTypeSimpleDescriptorRequest, Data: data}
	sdata, err := msg.Encode()
	if err != nil {
		return err
	}
	addTask(sdata)
	return nil
}

func (a *ZigbeeDescriptor) simpleDescriptionResponse(msg *Message) error {
	endpoint := msg.Data[5]
	profileId := fmt.Sprintf("%x", msg.Data[6:8])
	deviceId := fmt.Sprintf("%x", msg.Data[8:10])
	intputCnt := msg.Data[11]
	intputs := msg.Data[12 : 12+intputCnt*2]
	outputCnt := msg.Data[12+intputCnt*2]
	outputs := msg.Data[12+intputCnt*2+1 : 12+intputCnt*2+1+outputCnt*2]

	//log.Printf("intput(%d -> %x) ontputs(%d -> %x)\n", intputCnt, intputs, outputCnt, outputs)
	var descriptor *EndPointDescriptor
	for _, v := range a.Descriptors {
		if v.Endpoint == endpoint {
			descriptor = v
			break
		}
	}
	if descriptor == nil {
		descriptor = &EndPointDescriptor{
			Endpoint: endpoint,
		}
		a.Descriptors = append(a.Descriptors, descriptor)
	}

	descriptor.ProfileId = profileId
	descriptor.DeviceId = deviceId

	log.Info("mac(%s) net(%s) endpoint(%d) profile_id(%s) device_id(%s)", a.MacAddr, a.NetAddr, descriptor.Endpoint, descriptor.ProfileId, descriptor.DeviceId)

	for i := 0; i < int(intputCnt); i++ {
		intputId := fmt.Sprintf("%x", intputs[i*2:i*2+2])
		descriptor.InputCluster = append(descriptor.InputCluster, intputId)
	}

	for i := 0; i < int(outputCnt); i++ {
		outputId := fmt.Sprintf("%x", outputs[i*2:i*2+2])
		descriptor.OutputCluster = append(descriptor.OutputCluster, outputId)
	}

	if a.instance == nil {
		a.instance = a.newInstance()
	}
	return nil
}

func (a *ZigbeeDescriptor) readAttributeRequest(endpoint uint8, clusterId, attributeId string) error {
	data, _ := hex.DecodeString(fmt.Sprintf("02%s%02d%02d%s0000000001%s", a.NetAddr, 0x01, endpoint, clusterId, attributeId))
	msg := &Message{MsgT: SerialLinkMsgTypeReadAttributeRequest, Data: data}
	sdata, err := msg.Encode()
	if err != nil {
		return err
	}
	addTask(sdata)
	return nil
}

func (a *ZigbeeDescriptor) attributeResponseWithEndPoint(msg *Message) error {
	endpoint := msg.Data[3]
	clusterId := fmt.Sprintf("%x", msg.Data[4:6])
	attributeId := fmt.Sprintf("%x", msg.Data[6:8])

	var data []byte
	if msg.MsgT == SerialLinkMsgTypeAttributeResponse {
		count := binary.BigEndian.Uint16(msg.Data[9:11])
		if int(count)+11 != len(msg.Data) {
			return fmt.Errorf("read attribute response (%d:%d) invalid", count+11, len(msg.Data))
		}
		data = msg.Data[11:]
	} else {
		count := binary.BigEndian.Uint16(msg.Data[10:12])
		if int(count)+12 != len(msg.Data) {
			return fmt.Errorf("read attribute response (%d:%d) invalid", count+11, len(msg.Data))
		}
		data = msg.Data[12:]
	}

	log.Info("accessory(%s) netaddr(%s) cluster_id(%s) endpoint(%d) attribute_id(%s) value(%s)", a.MacAddr, a.NetAddr, clusterId, endpoint, attributeId, string(data))
	if clusterId == ZigbeeClusterIdBasic && attributeId == CldBaseAttrIdModeIdentifier {
		switch string(data) {
		case "lumi.sensor_ht":
			err := json.Unmarshal([]byte(xiaoMiHTSensorEndpointDefault), &a.Descriptors)
			if err == nil {
				a.EndPoints = []int{1, 2, 3}
				manager.save()
				if a.instance == nil {
					a.instance = a.newInstance()
				}
			}

		case "lumi.sensor_magnet":
			err := json.Unmarshal([]byte(xiaoMiContactSensorEndpointDefault), &a.Descriptors)
			if err == nil {
				a.EndPoints = []int{1}
				manager.save()
				if a.instance == nil {
					a.instance = a.newInstance()
				}
			}

		case "lumi.sensor_switch":
			err := json.Unmarshal([]byte(xiaoMiContactSensorEndpointDefault), &a.Descriptors)
			if err == nil {
				a.Descriptors[0].ModeIdentifier = "lumi.sensor_switch"
				a.EndPoints = []int{1}
				manager.save()
				if a.instance == nil {
					a.instance = a.newInstance()
				}
			}

		case "lumi.sensor_motion.aq2":
			err := json.Unmarshal([]byte(xiaoMiMotionSensorEndpointDefault), &a.Descriptors)
			if err == nil {
				a.EndPoints = []int{1}
				manager.save()
				if a.instance == nil {
					a.instance = a.newInstance()
				}
			}

		}
	}
	return nil
}

// 06 a8f3 01 0000 0005 4200 096c 756d692e706c7567
////74 41a9 01 0000 ff01 4200 1f01219f0b0421a8130521eb02062401000000006429f10a6521a8140a21cfd4
func (a *ZigbeeDescriptor) readAttributeResponse(msg *Message) error {
	endpoint := msg.Data[3]
	var descriptor *EndPointDescriptor
	for _, v := range a.Descriptors {
		if v.Endpoint == endpoint {
			descriptor = v
			break
		}
	}
	if descriptor == nil {
		if a.DevType == DevTypeEndDevice {
			return a.attributeResponseWithEndPoint(msg)
		}
		return nil
	}

	clusterId := fmt.Sprintf("%x", msg.Data[4:6])
	attributeId := fmt.Sprintf("%x", msg.Data[6:8])

	var data []byte
	if msg.MsgT == SerialLinkMsgTypeAttributeResponse {
		count := binary.BigEndian.Uint16(msg.Data[9:11])
		if int(count)+11 != len(msg.Data) {
			return fmt.Errorf("read attribute response (%d:%d) invalid", count+11, len(msg.Data))
		}
		data = msg.Data[11:]
	} else {
		count := binary.BigEndian.Uint16(msg.Data[10:12])
		if int(count)+12 != len(msg.Data) {
			return fmt.Errorf("read attribute response (%d:%d) invalid", count+11, len(msg.Data))
		}
		data = msg.Data[12:]
	}

	if clusterId == "0000" {
		switch attributeId {
		case CldBaseAttrIdManufacturerName:
			descriptor.Manufacture = string(data)
		case CldBaseAttrIdModeIdentifier:
			descriptor.ModeIdentifier = string(data)
		case CldBaseAttrIdDateCode:
			descriptor.DateCode = string(data)
		}
	}
	return nil
}

type AccessoryManager struct {
	netAddrMap  map[string]*ZigbeeDescriptor
	macAddrMap  map[string]*ZigbeeDescriptor
	accessories []*ZigbeeDescriptor
}

// 保存设备信息
func (m *AccessoryManager) save() error {
	data, err := json.Marshal(m.accessories)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(configFileDefault, data, 0666)
}

// 加载设备信息
func (m *AccessoryManager) load() error {
	data, err := ioutil.ReadFile(configFileDefault)
	if err != nil {
		log.Error("%s", err)
		return err
	}

	err = json.Unmarshal(data, &m.accessories)
	if err != nil {
		m.accessories = []*ZigbeeDescriptor{}
		m.netAddrMap = make(map[string]*ZigbeeDescriptor)
		m.macAddrMap = make(map[string]*ZigbeeDescriptor)
		log.Error("%s", err)
		return err
	}

	for _, v := range m.accessories {
		log.Info("accessory mac(%s) net(%s)", v.MacAddr, v.NetAddr)
		m.netAddrMap[v.NetAddr] = v
		m.macAddrMap[v.MacAddr] = v
		v.instance = v.newInstance()
	}
	return nil
}

func (m *AccessoryManager) Add(netAddr, macAddr string, devType string) error {
	var acc *ZigbeeDescriptor
	for _, v := range m.accessories {
		if v.MacAddr == macAddr {
			acc = v
			break
		}
	}

	// 跟新网络地址
	if acc != nil {
		if acc.NetAddr != netAddr {
			delete(m.netAddrMap, acc.NetAddr)
			acc.NetAddr = netAddr
			m.netAddrMap[acc.NetAddr] = acc
			m.save()
		}
		return nil
	}

	acc = &ZigbeeDescriptor{
		NetAddr: netAddr,
		MacAddr: macAddr,
		DevType: devType,
	}
	m.macAddrMap[macAddr] = acc
	m.netAddrMap[netAddr] = acc
	m.accessories = append(m.accessories, acc)
	m.save()

	if len(acc.EndPoints) == 0 {
		acc.activeEndPointRequest()
	}
	return nil
}

func (m *AccessoryManager) zigbeeDeviceAnnounceResponse(msg *Message) error {
	if len(msg.Data) < 10 {
		return nil
	}

	netAddr := fmt.Sprintf("%x", msg.Data[0:2])
	macAddr := fmt.Sprintf("%x", msg.Data[2:10])
	capability := msg.Data[10]
	devType := DevTypeEndDevice
	if capability&(0x01<<1) != 0x00 {
		devType = DevTypeRouter
	}
	m.Add(netAddr, macAddr, devType)
	return nil
}

func (m *AccessoryManager) zigbeeEndpointCheck() bool {
	ret := true
	for _, acc := range m.accessories {
		if acc.DevType != DevTypeRouter {
			continue
		}

		if len(acc.EndPoints) == 0 {
			log.Info("netaddr(%s) endpoint request", acc.NetAddr)
			acc.activeEndPointRequest()
			time.Sleep(5000 * time.Millisecond)
			ret = false
			continue
		}

		for _, endpoint := range acc.EndPoints {
			var descriptor *EndPointDescriptor
			for _, v := range acc.Descriptors {
				if v.Endpoint == uint8(endpoint) {
					descriptor = v
					break
				}
			}
			if descriptor == nil {
				ret = false
				acc.simpleDescriptionRequest(uint8(endpoint))
				time.Sleep(1000 * time.Millisecond)
			} else {
				switch descriptor.DeviceId {
				case ZigbeeDeviceZHASmartPlug, ZigbeeDeviceZLLColourTemperatureLight, ZigbeeDeviceZHAColourTemperatureLight:
					if descriptor.Manufacture == "" {
						acc.readAttributeRequest(uint8(endpoint), ZigbeeClusterIdBasic, CldBaseAttrIdManufacturerName)
						time.Sleep(1000 * time.Millisecond)
						ret = false
					}
					if descriptor.ModeIdentifier == "" {
						acc.readAttributeRequest(uint8(endpoint), ZigbeeClusterIdBasic, CldBaseAttrIdModeIdentifier)
						time.Sleep(1000 * time.Millisecond)
						ret = false
					}
					if descriptor.DateCode == "" {
						acc.readAttributeRequest(uint8(endpoint), ZigbeeClusterIdBasic, CldBaseAttrIdDateCode)
						time.Sleep(1000 * time.Millisecond)
						ret = false
					}
				}
			}
		}
	}
	return ret
}

func (m *AccessoryManager) activeEndpointRequestResponse(msg *Message) error {
	if len(msg.Data) < 5 {
		return nil
	}

	netAddr := fmt.Sprintf("%x", msg.Data[2:4])
	acc, ok := m.netAddrMap[netAddr]
	if !ok {
		m.macAddrRequest(netAddr)
		return fmt.Errorf("netaddr(%s) 404 not found", netAddr)
	}
	count := msg.Data[4]
	if len(msg.Data) != 5+int(count) {
		return nil
	}
	endpoints := msg.Data[5 : 5+count]
	for _, endpoint := range endpoints {
		acc.EndPoints = append(acc.EndPoints, int(endpoint))
		acc.simpleDescriptionRequest(endpoint)
	}
	m.save()
	return nil
}

func (m *AccessoryManager) endpointSimpleDescriptionResponse(msg *Message) error {
	if len(msg.Data) < 13 {
		return nil
	}

	netAddr := fmt.Sprintf("%x", msg.Data[2:4])
	acc, ok := m.netAddrMap[netAddr]
	if !ok {
		m.macAddrRequest(netAddr)
		return fmt.Errorf("netaddr(%s) 404 not found", netAddr)
	}
	err := acc.simpleDescriptionResponse(msg)
	if err == nil {
		m.save()
	}
	return nil
}

//74 41a9 01 0000 ff0142001f01219f0b0421a8130521eb02062401000000006429f10a6521a8140a21cfd4
//5c 41a9 01 0402 0000 2900020a28
func (m *AccessoryManager) readAttributeResponse(msg *Message) error {
	if len(msg.Data) < 12 {
		return nil
	}

	netAddr := fmt.Sprintf("%x", msg.Data[1:3])
	acc, ok := m.netAddrMap[netAddr]
	if !ok {
		m.macAddrRequest(netAddr)
		return fmt.Errorf("netaddr(%s) 404 not found", netAddr)
	}

	clusterId := fmt.Sprintf("%x", msg.Data[4:6])
	var err error
	if clusterId == ZigbeeClusterIdBasic {
		err = acc.readAttributeResponse(msg)
	} else {
		if acc.instance != nil {
			acc.instance.update(msg.Data)
		}
	}
	if err != nil {
		return err
	}
	m.save()
	return nil
}

func (m *AccessoryManager) requestDefaultResponse(msg *Message) error {
	srcAddr := binary.BigEndian.Uint16(msg.Data[1:])
	netAddr := fmt.Sprintf("%x", msg.Data[1:3])
	endpoint := msg.Data[3]
	data := msg.Data[7]
	log.Printf("response: netaddr(%04x) endpoint(%d) data(%d)\n", srcAddr, endpoint, data)
	acc, ok := m.netAddrMap[netAddr]
	if !ok {
		m.macAddrRequest(netAddr)
		return fmt.Errorf("netaddr(%s) 404 not found", netAddr)
	}

	if acc.instance != nil {
		acc.instance.update(msg.Data)
	}
	return nil
}

func (m *AccessoryManager) leaveIndication(msg *Message) error {
	macAddr := fmt.Sprintf("%x", msg.Data[0:8])
	rejoin := msg.Data[8]
	// 配件退网
	if rejoin == 0x00 {
		acc, ok := m.macAddrMap[macAddr]
		if ok {
			delete(m.macAddrMap, macAddr)
			delete(m.netAddrMap, acc.NetAddr)
			for i, v := range m.accessories {
				if v.MacAddr == macAddr {
					m.accessories = append(m.accessories[:i], m.accessories[i+1:]...)
					break
				}
			}
			m.save()
			log.Notice("accessory(%s) netaddr(%s) leave not rejoin", acc.MacAddr, acc.NetAddr)
			container.RemoveAccessoryByAid(macAddr)
		}
	} else {
		log.Notice("accessory(%s) leave and rejoin", macAddr)
	}
	return nil
}

func (m *AccessoryManager) macAddrReadResponse(msg *Message) error {
	macAddr := fmt.Sprintf("%x", msg.Data[2:10])
	netAddr := fmt.Sprintf("%x", msg.Data[10:12])
	log.Info("macaddr(%s) netaddr(%s)", macAddr, netAddr)
	m.managementLeaveRequest(netAddr, macAddr, true)
	return nil
}

// 查找mac地址
//01 80 41 00 0F 9B 15 00 00 15 8D 00 01 D2 6E BB BA 28 00 00 4C 03
//01 00 41 00 00 47 BA 28 BA 28 00 00 03
func (m *AccessoryManager) macAddrRequest(netAddr string) error {
	netaddr, _ := hex.DecodeString(netAddr)
	data := []byte{netaddr[0], netaddr[1], netaddr[0], netaddr[1], 0x01, 0x00}
	msg := &Message{MsgT: SerialLinkMsgMacAddressRequest, Data: data}
	sdata, err := msg.Encode()
	if err != nil {
		return err
	}
	addTask(sdata)
	return nil
}

func (m *AccessoryManager) managementLeaveRequest(netAddr, macAddr string, rejoin bool) error {
	net, _ := hex.DecodeString(netAddr)
	mac, _ := hex.DecodeString(macAddr)
	data := make([]byte, 0)
	data = append(data, net[0:]...)
	data = append(data, mac[0:]...)
	if rejoin {
		data = append(data, 0x01, 0x00)
	} else {
		data = append(data, 0x00, 0x00)
	}
	msg := &Message{MsgT: SerialLinkMsgManagementLeaveRequest, Data: data}
	sdata, err := msg.Encode()
	if err != nil {
		return err
	}
	addTask(sdata)
	return nil
}

func (m *AccessoryManager) requestAttributeResponse(msg *Message) error {
	return nil
}

var manager = AccessoryManager{
	netAddrMap: make(map[string]*ZigbeeDescriptor),
	macAddrMap: make(map[string]*ZigbeeDescriptor),
}

func zigbeeManagerInit() {
	manager.load()
	addObserver(SerialLinkDeviceAnnounce, manager.zigbeeDeviceAnnounceResponse)
	addObserver(SerialLinkActiveEndPointResponse, manager.activeEndpointRequestResponse)
	addObserver(SerialLinkMsgTypeSimpleDescriptorResponse, manager.endpointSimpleDescriptionResponse)
	addObserver(SerialLinkMsgTypeReadAttributeResponse, manager.readAttributeResponse)
	addObserver(SerialLinkMsgTypeAttributeResponse, manager.readAttributeResponse)
	addObserver(SerialLinkMsgTypeDefaultResponse, manager.requestDefaultResponse)
	addObserver(SerialLinkMsgLeaveIndication, manager.leaveIndication)
	addObserver(SerialLinkMsgMacAddressResponse, manager.macAddrReadResponse)

	go func() {
		for {
			time.Sleep(time.Second)
			manager.zigbeeEndpointCheck()
		}
	}()
}
